﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GuiLibrary.Components;
using Imaginarium.team;
using Imaginarium.battleSistem;
using Microsoft.Xna.Framework.Graphics;
using GuiLibrary.layouts;
using Microsoft.Xna.Framework;
using GameEngine.Scenes;
using Microsoft.Xna.Framework.Input;
using GuiLibrary;

namespace Imaginarium.gui
{
    public class BattleResultGui: Gui
    {
        TurnExpert turnExpert;
        public bool applyExp = false;
        public float expApplyRate = GUIParameters.expApplyRatePerMilisecond;
        public bool expApplied = false;
        #region Constructors
        public BattleResultGui(Panel panel, TurnExpert turnExpert) :
        base(panel)
        {
            this.turnExpert = turnExpert;
        }
        public BattleResultGui(TurnExpert turnExpert) :
            base() 
        {
            this.turnExpert = turnExpert;
        }
        #endregion

        public override void LoadContent()
        {
            #region ObtainRequiredData
            Team enemyTeam = turnExpert.m_Teams[0], playerTeam = turnExpert.m_Teams[1];
            List<Element> playerResultElements = new List<Element>(playerTeam.characters.Count);
            int totalBaseExpDropped = 0, totalJobExpDropped = 0;
            int[] exps;
            foreach (Character character in enemyTeam.characters) 
            {
                exps = character.calcExpDropped();
                totalBaseExpDropped += exps[0];
                totalJobExpDropped += exps[1];
            }
            DTOExpGained expGained;
            int playersAlive = 0;
            foreach (Character character in playerTeam.characters)
                if (!character.isDead())
                    playersAlive++; //only count the alives
            foreach (Character character in playerTeam.characters)
            {
                if (!character.isDead() && character is Player) //give exp only to alive players, but dividing among alive non-players too
                {
                    Player player = (Player) character;
                    expGained = new DTOExpGained(player, (int)Math.Ceiling(totalBaseExpDropped / (float)playersAlive),
                        player.exp, (int)Math.Ceiling(totalJobExpDropped/(float)playersAlive), player.currentJob.jobExp);
                    playerResultElements.Add(new Element(expGained.player.name, expGained));
                }
            }
            #endregion

            #region CreateGuiComponents
            basePanel = new Panel(position, GUIParameters.backgroundBattleResultGUI());
            SpriteFont font = Imaginarium.ImaginariumGame.getInstance().Content.Load<SpriteFont>(CodeParameters.battleResultFont);
            string[] colNames = GUIParameters.ColNameBR();
            foreach(string colName in colNames)
                basePanel.components.Add(new Label(basePanel, new Text(font, colName, Color.White, GameEngine.animations.OriginType.LEFT_UP)));
            foreach (Element element in playerResultElements) 
            {
                DTOExpGained dto = (DTOExpGained)element.associatedObject;
                basePanel.components.Add(new Label(basePanel, new Vector2(), new Text(font, dto.player.name, Color.White, GameEngine.animations.OriginType.LEFT_UP), dto));
                basePanel.components.Add(new Label(basePanel, new Vector2(), new Text(font, dto.expGained.ToString(), Color.White, GameEngine.animations.OriginType.LEFT_UP), dto));
                basePanel.components.Add(new Label(basePanel, new Vector2(), new Text(font, dto.currentExp.ToString(), Color.White, GameEngine.animations.OriginType.LEFT_UP), dto));
                basePanel.components.Add(new Label(basePanel, new Vector2(), new Text(font, dto.jobExpGained.ToString(), Color.White, GameEngine.animations.OriginType.LEFT_UP), dto));
                basePanel.components.Add(new Label(basePanel, new Vector2(), new Text(font, dto.currentJobExp.ToString(), Color.White, GameEngine.animations.OriginType.LEFT_UP), dto));
            }
            basePanel.layout = new GridLayout(basePanel, GUIParameters.marginsBattleResultGUI, playerResultElements.Count + 1, 5, null, null, null); 
            basePanel.refresh();
            SceneManager.getInstance().enabledInput = true;
            #endregion
        }
        public override void onSelectedElement(Element e)
        {
            //No selectable elements
        }
        public override void handleInput() //General input (independant of the components)
        {
            InputManager im = InputManager.getInstance();
            if (im.isNewKeyPressed(Keys.Enter))
                applyExp = true;
            if ((im.isNewKeyPressed(Keys.Enter) || im.isNewKeyPressed(Keys.Escape))
                && expApplied)
                ImaginariumGame.getInstance().Exit();
            base.handleInput();
        }
        public override void update(GameTime gameTime) //Substract exp to the gained amount and add it to the players' exp
        {
            if(applyExp)
                updateExps(gameTime.ElapsedGameTime.Milliseconds);
            base.update(gameTime);
        }
        private void updateExps(int miliElapsed) 
        {
            GridLayout expGrid = (GridLayout)basePanel.layout;
            int playerAmount = expGrid.rows - 1;
            bool finishedUpdating = true;
            for (int i = 0; i < playerAmount; i++)
            {
                int playerIndex = expGrid.cols * (1 + i); //player's index within the components
                DTOExpGained dto = (DTOExpGained)((Label)basePanel.components[playerIndex]).elementAssociated;
                dto.expGainedApplied += (int)Math.Ceiling(miliElapsed * expApplyRate * dto.expGained);
                if (dto.expGainedApplied > dto.expGained) dto.expGainedApplied = dto.expGained;
                dto.jobExpGainedApplied += (int)Math.Ceiling(miliElapsed * expApplyRate * dto.jobExpGained);
                if (dto.jobExpGainedApplied > dto.jobExpGained) dto.jobExpGainedApplied = dto.jobExpGained;
                if (dto.expGainedApplied != dto.expGained || dto.jobExpGainedApplied != dto.jobExpGained)
                    finishedUpdating = false;

                refreshExpComponentAndPlayer(i);
            }
            basePanel.refresh();
            if (finishedUpdating)
            {
                applyExp = false;
                expApplied = true;
            }
        }
        //Refresh a single Player
        public void refreshExpComponentAndPlayer(int playerRow) 
        {
            GridLayout expGrid = (GridLayout)basePanel.layout;
            int playerIndex = expGrid.cols * (1 + playerRow); //player's index within the components
            DTOExpGained dto = (DTOExpGained)((Label)basePanel.components[playerIndex]).elementAssociated;
            int expToApply = dto.expGained - dto.expGainedApplied, actualExp = dto.currentExp + dto.expGainedApplied;
            int jobExpToApply = dto.jobExpGained - dto.jobExpGainedApplied, actualJobExp = dto.currentJobExp + dto.jobExpGainedApplied;
            ((Label)basePanel.components[playerIndex + 1]).text.text = (expToApply).ToString();
            ((Label)basePanel.components[playerIndex + 2]).text.text = (actualExp).ToString();
            ((Label)basePanel.components[playerIndex + 3]).text.text = (jobExpToApply).ToString();
            ((Label)basePanel.components[playerIndex + 4]).text.text = (actualJobExp).ToString();
            dto.player.exp = actualExp;
            dto.playerCurJob.jobExp = actualJobExp;
        }
        //Refresh All
        public void refreshExpsComponentsAndPlayers()
        {
            GridLayout expGrid = (GridLayout)basePanel.layout;
            int playerAmount = expGrid.rows - 1;
            for (int i = 0; i < playerAmount; i++)
            {
                refreshExpComponentAndPlayer(i);
            }
            basePanel.refresh();
        }
        public class DTOExpGained: Element
        {
            #region Attributes
            public Player player { get; set; }
            public DetailJob playerCurJob { get; set; } //exist to prevent a change of job while applying exp
            public int expGained { get; set; }
            public int expGainedApplied { get; set; }
            public int currentExp { get; set; }
            public int jobExpGained { get; set; }
            public int jobExpGainedApplied { get; set; }
            public int currentJobExp { get; set; }
            #endregion
            #region Constructors
            public DTOExpGained(Player player, int expGained, int currentExp, int jobExpGained, int currentJobExp) :
                base(player.name, player)
            {
                this.player = player;
                this.playerCurJob = player.currentJob;
                this.expGained = expGained;
                this.currentExp = currentExp;
                this.jobExpGained = jobExpGained;
                this.currentJobExp = currentJobExp;
                this.expGainedApplied = 0;
                this.jobExpGainedApplied = 0;
            }
            #endregion
        }
    }
}
